#include "pch.hpp"
#include "AutoCompletion.hpp"

#include <QtUi/Application.hpp>

using namespace Engine;
using namespace Engine::Edit;




AutoCompletion::AutoCompletion()
	: m_compiler(QtUi::Application::instance()->serviceProvider())
	, m_changes(0)
{
	//
	// Signals
	//

	verify(connect(&m_timer, SIGNAL(timeout()), SLOT(timer())));

	m_timer.setInterval(100);
	m_timer.start();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

AutoCompletion::~AutoCompletion()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void AutoCompletion::setProject(Project::ptr project)
{
	m_project = project;
	m_contents.clear();

	if(!m_project)
		return;

	project->scripts()->for_each_item([&](ProjectItem::const_ptr item)
	{
		auto script = boost::shared_dynamic_cast<const Script>(item);
		if(script)
			m_contents[script->name()] = script->content();
	});

	++m_changes;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void AutoCompletion::scriptWindowOpened(const ScriptEditWindow* window)
{
	m_errors.insert(std::make_pair(window->script()->name(), window->errorModel()));
	++m_changes;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void AutoCompletion::scriptWindowChanged(const ScriptEditWindow* window)
{
	QString name     = window->script()->name();
	QString contents = window->contents();

	auto it = m_contents.find(name);
	if(it != m_contents.end())
		if(it->second == contents)
			return;
		else
			it->second = contents;
	else
		m_contents.insert(std::make_pair(name, contents));

	++m_changes;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void AutoCompletion::scriptWindowClosed(const ScriptEditWindow* window)
{
	auto it = m_errors.find(window->script()->name());
	if(it != m_errors.end())
		m_errors.erase(it);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void AutoCompletion::timer()
{
	if(m_compilations.size() > 0)
	{
		// we only care about the latest compilations, everything
		// before that
		auto compilation = m_compilations.back();
		if(compilation.isReady())
		{
			const auto& result = compilation();
			auto module = result.module();

			// Collect type information
			if(module)
			{
				int numTypes = module->typeCount();

				m_types.clear();
				m_types = module->types();
			}

			// Reset error models
			foreach(auto& pair, m_errors)
			{
				pair.second->clear();
			}

			// Distribute messages amongst error models
			foreach(const auto& message, result.messages())
			{
				auto it = m_errors.find(message.file);
				if(it != m_errors.end())
					it->second->add(message);
			}

			m_compilations.clear();
		}
	}
	else if(m_compilations.size() == 0 && m_project && m_contents.size() > 0 && m_changes > 0)
	{
		// Compiling might be rejected by the compiler, we have to check if he's
		// actually compiling stuff
		auto compilation = m_compiler.compile(m_contents);
		if(compilation)
		{
			// Compilation is on the way... now we can reset the changes
			m_changes = 0;
			m_compilations.push_back(*compilation);
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void AutoCompletion::compiled(CompilationResult result)
{
	m_types = m_compiler.types();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
